package net.sh.dpinfrastructure.mongo;

import static net.sh.dpinfrastructure.mongo.DPMongoOperater.AND;
import static net.sh.dpinfrastructure.mongo.DPMongoOperater.GT;
import static net.sh.dpinfrastructure.mongo.DPMongoOperater.GTE;
import static net.sh.dpinfrastructure.mongo.DPMongoOperater.IN;
import static net.sh.dpinfrastructure.mongo.DPMongoOperater.LT;
import static net.sh.dpinfrastructure.mongo.DPMongoOperater.LTE;
import static net.sh.dpinfrastructure.mongo.DPMongoOperater.NEQ;
import static net.sh.dpinfrastructure.mongo.DPMongoOperater.NIN;
import static net.sh.dpinfrastructure.mongo.DPMongoOperater.NOT;
import static net.sh.dpinfrastructure.mongo.DPMongoOperater.OR;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.persistence.Column;
import javax.persistence.Table;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.bson.Document;

import com.mongodb.client.FindIterable;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.model.UpdateOptions;
import com.mongodb.client.result.UpdateResult;

import net.sh.dpinfrastructure.db.DPCriteriaType;
import net.sh.dpinfrastructure.db.DPOrderType;
import net.sh.dpinfrastructure.db.DPPageAndSort;
import net.sh.dpinfrastructure.db.DPSearchStatement;
import net.sh.dpinfrastructure.db.DPSystemFields;
import net.sh.dpinfrastructure.db.DPWriteResult;
import net.sh.dpinfrastructure.db.DPWriteResultType;
import net.sh.dpinfrastructure.exception.DPException;


/**
 * 
 * {@link MongoCollection} 包装类
 * 
 * @version 1.0
 * @since JDK1.7
 * @author shaohui
 * @company 上海朝阳永续信息技术有限公司
 * @copyright (c) 2015 SunTime Co'Ltd Inc. All rights reserved.
 * @date 2015年8月27日 下午1:30:20
 */
public class DPCollection {
    
    /**
     * 
     * 将通用查询语句 {@link DPSearchStatement} 转换成mongo查询语句 {@link DPDocument}
     *
     * @param search
     * @return
     * 
     * @author shaohui
     * @date 2017年1月24日 下午2:06:57
     */
    public static DPDocument buildMongoQuery(DPSearchStatement criteria) {
        DPDocument target = new DPDocument();
        if (criteria == null) {
            return target;
        }
        LinkedHashMap<DPCriteriaType, Map<String, ? extends Object>> criteriaMap = criteria.toMap();
        if (MapUtils.isEmpty(criteriaMap)) {
            return target;
        }
        List<DPDocument> andElements = new ArrayList<>();
        List<DPDocument> orElements = new ArrayList<>();
        for (Map.Entry<DPCriteriaType, Map<String, ? extends Object>> criteriaItem : criteriaMap.entrySet()) {
            DPCriteriaType criteriaType = criteriaItem.getKey();
            Map.Entry<String, ? extends Object> fieldValueEntry = criteriaItem.getValue().entrySet().iterator().next();
            String field = fieldValueEntry.getKey();
            Object value  = fieldValueEntry.getValue();
            switch (criteriaType) {
                case EQ: {
                    target.append(field, value);
                    break;
                }
                case NEQ: {
                    target.append(field, new DPDocument(NEQ, value));
                    break;
                }
                case GT: {
                    target.append(field, new DPDocument(GT, value));
                    break;
                }
                case GTE: {
                    target.append(field, new DPDocument(GTE, value));
                    break;
                }
                case LT: {
                    target.append(field, new DPDocument(LT, value));
                    break;
                }
                case LTE: {
                    target.append(field, new DPDocument(LTE, value));
                    break;
                }
                case IN: {
                    target.append(field, new DPDocument(IN, value));
                    break;
                }
                case NIN: {
                    target.append(field, new DPDocument(NIN, value));
                    break;
                }
                case BETWEEN: {
                    List<?> betweenValues = (List<?>) value;
                    Object smallerValue = betweenValues.get(0);
                    Object biggerValue = betweenValues.get(1);
                    target.append(field, new DPDocument(GT, smallerValue).append(LT, biggerValue));
                    break;
                }
                case LBETWEEN: {
                    List<?> betweenValues = (List<?>) value;
                    Object smallerValue = betweenValues.get(0);
                    Object biggerValue = betweenValues.get(1);
                    target.append(field, new DPDocument(GTE, smallerValue).append(LT, biggerValue));
                    break;
                }
                case RBETWEEN: {
                    List<?> betweenValues = (List<?>) value;
                    Object smallerValue = betweenValues.get(0);
                    Object biggerValue = betweenValues.get(1);
                    target.append(field, new DPDocument(GT, smallerValue).append(LTE, biggerValue));
                    break;
                }
                case LRBETWEEN: {
                    List<?> betweenValues = (List<?>) value;
                    Object smallerValue = betweenValues.get(0);
                    Object biggerValue = betweenValues.get(1);
                    target.append(field, new DPDocument(GTE, smallerValue).append(LTE, biggerValue));
                    break;
                }
                case AND: {
                    DPSearchStatement andCriteria = (DPSearchStatement) value;
                    andElements.add(buildMongoQuery(andCriteria));
                    target.append(AND, andElements);
                    break;
                }
                case OR: {
                    DPSearchStatement orCriteria = (DPSearchStatement) value;
                    orElements.add(buildMongoQuery(orCriteria));
                    target.append(OR, orElements);
                    break;
                }
                case NOT: {
                    DPSearchStatement notCriteria = (DPSearchStatement) value;
                    target.append(NOT, buildMongoQuery(notCriteria));
                    break;
                }
                default:
                    break;
            }
        }
        return target;
    }
    
    private MongoCollection<Document> collection;
    
    /**
     * @param collection 被包装的collection
     */
    protected DPCollection(MongoCollection<Document> collection) {
        if (collection == null) {
            throw new NullPointerException();
        }
        this.collection = collection;
    }
    
    /**
     * 
     * 聚合查询
     * 
     * @return
     *
     * @author shaohui
     * @date 2016年7月31日上午10:06:48
     * @since JDK 1.8
     */
    public DPCursor aggregate(List<DPDocument> pipeline) {
        List<Document> originalPipeline = new ArrayList<>();
        for (DPDocument document : pipeline) {
            if (document == null) {
                continue;
            }
            originalPipeline.add(document.toDocument());
        }
        return new DPCursor(collection.aggregate(originalPipeline).iterator());
    }
    
    /**
     * 
     * 查询记录总条数
     *
     * @return
     * 
     * @author shaohui
     * @date 2016年4月15日 上午10:02:42
     */
    public long count() {
        return collection.count();
    }
    
    /**
     * 
     * 查询记录总条数
     *
     * @return
     * 
     * @author shaohui
     * @date 2016年4月15日 上午10:00:52
     */
    public long count(final DPDocument criteria) {
        if (criteria == null) {
            return collection.count();
        }
        return collection.count(criteria.toDocument());
    }
    
    /**
     * 
     * 删除（单条记录）
     *
     * @param criteria
     * 
     * @author shaohui
     * @date 2016年4月14日 下午7:52:06
     */
    public void delete(DPDocument criteria) {
        delete(criteria, false, true);
    }
    
    /**
     * 
     * 删除
     * @param multiple 是否删除多条
     * 
     * @author shaohui
     * @date 2016年4月14日 下午7:48:13
     */
    public void delete(DPDocument criteria, boolean multiple) {
        delete(criteria, multiple, false);
    }
    
    /**
     * 
     * 删除
     *
     * @param criteria 查询条件
     * @param multiple 是否删除多条
     * @param logically true：逻辑删除，false：物理删除
     * 
     * @author shaohui
     * @date 2016年4月14日 下午8:10:54
     */
    public void delete(DPDocument criteria, boolean multiple, boolean logically) {
        if (logically) { //逻辑删除
            update(criteria, new DPDocument("$set", new DPDocument("_status", DPSystemFields.DELETE_FLAG)), false, multiple, true);
            return;
        }
        //物理删除
        if (multiple) {
            collection.deleteMany(criteria.toDocument());
            return;
        }
        collection.deleteOne(criteria.toDocument());
    }
    
    /**
     * 
     * 普通查询
     * @see DPCollection#find(DPDocument, DPDocument, DPPageAndSort, boolean)
     * 
     * @return
     *
     * @author shaohui
     * @date 2017年2月19日上午10:07:53
     * @since JDK 1.8
     */
    public DPCursor find() {
        return find(null, null, null, false);
    }
    
    /**
     * 
     * 普通查询
     * 
     * @param criteria
     * @return
     *
     * @author shaohui
     * @date 2017年2月19日上午10:07:58
     * @since JDK 1.8
     */
    public DPCursor find(DPDocument criteria) {
        return find(criteria, null, null, false);
    }
    
    /**
     * 
     * 普通查询<br>
     * 使用默认的排序规则及分页规则查询，包括逻辑删除项
     * @see #find(DPDocument, Set, DPPageAndSort, boolean)
     * 
     * @param criteria
     * @param fields
     * @return
     *
     * @author shaohui
     * @date 2017年2月19日上午10:08:14
     * @since JDK 1.8
     */
    public DPCursor find(DPDocument criteria, Set<String> fields) {
        return find(criteria, fields, null, false);
    }
    
    /**
     * 
     * 普通查询<br>
     * 逻辑删除项也查询
     * @see DPCollection#find(DPDocument, Set, DPPageAndSort, boolean)，
     * 
     * @param criteria
     * @param fields
     * @param pageAndSort
     * @return
     *
     * @author shaohui
     * @date 2017年2月19日上午10:38:52
     * @since JDK 1.8
     */
    public DPCursor find(DPDocument criteria, Set<String> fields, DPPageAndSort pageAndSort) {
        return find(criteria, fields, pageAndSort, false);
    }

    /**
     * 
     * 普通查询
     * 
     * @param criteria 查询条件，可以为空，表示查询全部
     * @param fields 查询字段，可以为空，表示查询全部字段
     * @param pageAndSort 分页及排序规则，可以为空，使用库表默认排序规则
     * @param logically 是否忽略逻辑删除的记录，true：忽略，false：不忽略
     * @return
     *
     * @author shaohui
     * @date 2017年2月19日上午10:11:03
     * @since JDK 1.8
     */
    public DPCursor find(DPDocument criteria, Set<String> fields, DPPageAndSort pageAndSort, boolean logically) {
        FindIterable<Document>  findIterable = null;
        if (criteria != null) {
            if (logically) {
                criteria.append(DPSystemFields.STATUS, new DPDocument("$ne", DPSystemFields.DELETE_FLAG));
            }
            findIterable = collection.find(criteria.toDocument());
        } else {
            if (logically) {
                findIterable = collection.find(new Document(DPSystemFields.STATUS, new Document("$ne", DPSystemFields.DELETE_FLAG)));
            } else {
                findIterable = collection.find();
            }
        }
        if (!CollectionUtils.isEmpty(fields)) {
            Document fieldsDocument = new Document();
            for (String field : fields) {
                if (StringUtils.isEmpty(field)) {
                    continue;
                }
                fieldsDocument.append(field, 1);
            }
            if (!fieldsDocument.isEmpty()) { // 如果设置了要返回的字段则不再返回"_id"字段
                fieldsDocument.append("_id", 0);
                findIterable.projection(fieldsDocument);
            }
        }
        if (pageAndSort == null) {
            findIterable.limit(DPPageAndSort.DEFAULT_ROWS); // 设置查询条数
            return new DPCursor(findIterable.iterator());
        }
        
        // 分页查询
        findIterable.skip(pageAndSort.getPage() * pageAndSort.getRows());
        findIterable.limit(pageAndSort.getRows());
        
        // 排序
        String orderFiled = pageAndSort.getOrderField();
        DPOrderType orderType = pageAndSort.getOrderType();
        LinkedHashMap<String, DPOrderType> orders = pageAndSort.getOrders();
        if (MapUtils.isEmpty(orders) && (StringUtils.isEmpty(orderFiled))) { // 没有排序规则
            return new DPCursor(findIterable.iterator());
        }
        Document order = new Document();
        if (StringUtils.isNotEmpty(orderFiled)) {
            order.append(orderFiled, (orderType == DPOrderType.ASC) ? 1 : -1);
        }
        if (MapUtils.isNotEmpty(orders)) {
            for (Map.Entry<String, DPOrderType> entry : orders.entrySet()) {
                String tempOrderField = entry.getKey();
                DPOrderType tempOrderType = entry.getValue();
                if (StringUtils.isEmpty(tempOrderField) || tempOrderType == null) {
                    continue;
                }
                order.append(tempOrderField, (tempOrderType == DPOrderType.ASC) ? 1 : -1);
            }
        }
        if (!order.isEmpty()) { // 其他原因导致的排序规则为空
            findIterable.sort(order);
        }
        return new DPCursor(findIterable.iterator()); 
    }
    
    /**
     * 
     * 查询单条记录
     *
     * @return
     * 
     * @author shaohui
     * @date 2016年4月14日 下午1:20:25
     */
    public DPDocument findDocument() {
        return findDocument(null, null, null, false);
    }
    
    /**
     * 
     * 查询单条记录
     *
     * @param criteria 查询条件
     * @return
     * 
     * @author shaohui
     * @date 2016年4月14日 下午1:48:30
     */
    public DPDocument findDocument(DPDocument criteria) {
        return findDocument(criteria, null, null, false);
    }
    
    /**
     * 
     * 查询单条记录
     *
     * @return
     * 
     * @author shaohui
     * @date 2016年4月14日 下午1:19:52
     */
    public DPDocument findDocument(DPDocument criteria, Set<String> fields) {
        return findDocument(criteria, fields, null, false);
    }
    
    /**
     * 
     * 查询单条记录
     *
     * @param criteria 查询条件
     * @param fields 返回字段
     * @param sort 排序规则
     * @return
     * 
     * @author shaohui
     * @date 2016年4月14日 下午1:49:20
     */
    public DPDocument findDocument(DPDocument criteria, Set<String> fields, DPPageAndSort sort, boolean logically) {
        if (sort != null && sort.getRows() != 1) {
            throw new IllegalArgumentException("more than one row need to be returned");
        }
        DPCursor cursor = find(criteria, fields, sort, logically);
        try {
            if (cursor.hasNext()) {
                return cursor.next();
            }
            return null;
        } finally {
            cursor.close();
        }
    }
    
    /**
     * 
     * 插入新的文档
     *
     * @param object 带插入的文档
     * 
     * @author shaohui
     * @date 2016年4月14日 下午7:21:55
     */
    public void insert(DPDocument object) {
        insert(object, true);
    }
    
    /**
     * 
     * 插入新的文档
     *
     * @param document 待插入文档
     * @param withInsertTime 是否记录插入时间
     * 
     * @author shaohui
     * @date 2016年4月14日 下午7:29:24
     */
    public void insert(DPDocument document, boolean withInsertTime) {
        if (document == null) {
            throw new NullPointerException("document");
        }
        if (withInsertTime) {
            document.append(DPSystemFields.INSERT_TIME, new Date());
        }
        document.append(DPSystemFields.STATUS, DPSystemFields.EXIST_FLAG);
        collection.insertOne(document.toDocument());
        if (withInsertTime) {
            document.remove(DPSystemFields.INSERT_TIME);
        }
        document.remove(DPSystemFields.STATUS);
    }
    
    /**
     * 
     * 插入新的文档
     *
     * @param objects
     * 
     * @author shaohui
     * @date 2016年4月14日 下午7:27:58
     */
    public void insert(List<DPDocument> objects) {
        insert(objects, false);
    }
    
    /**
     * 
     * 插入新的文档
     *
     * @param documents 待插入文档
     * @param withInsertTime 是否记录插入时间
     * 
     * @author shaohui
     * @date 2016年4月14日 下午7:33:15
     */
    public void insert(List<DPDocument> documents, boolean withInsertTime) {
        if (CollectionUtils.isEmpty(documents)) {
            throw new IllegalArgumentException("empty documents");
        }
        List<Document> insertingDocuments = new ArrayList<Document>();
        for (DPDocument object : documents) {
            if (object != null) {
                insertingDocuments.add(object.toDocument().
                        append(DPSystemFields.INSERT_TIME, new Date()).append(DPSystemFields.STATUS, DPSystemFields.EXIST_FLAG));
            }
        }
        if (!insertingDocuments.isEmpty()) {
            collection.insertMany(insertingDocuments);
        }
    }
    
    /**
     * 
     * 批量插入 <br/> 
     * 
     * @param objects 不能为<i>null</i>或者空集合
     * @param withInsertingTime
     * @param mapper 不能为<i>null</i>
     *
     * @author shaohui
     * @date 2017年2月19日下午12:08:29
     * @since JDK 1.8
     */
    public <T> void insert(List<T> objects, boolean withInsertingTime, DPObjectMapper<T> mapper) {
        if (CollectionUtils.isEmpty(objects)) {
            throw new IllegalArgumentException("empty objects");
        }
        if (mapper == null) {
            throw new NullPointerException("mapper");
        }
        List<DPDocument> documents = new ArrayList<>();
        for (T object : objects) {
            documents.add(mapper.mapper(object));
        }
        insert(documents, withInsertingTime);
    }
    
    /**
     * 
     * 批量插入<br/> 
     * 
     * @param objects
     * @param mapper
     *
     * @author shaohui
     * @date 2017年2月19日下午12:09:37
     * @since JDK 1.8
     */
    public <T> void insert(List<T> objects, DPObjectMapper<T> mapper) {
        insert(objects, true, mapper);
    }
    
    /**
     * 
     * 插入新的文档
     * 
     * @param object
     * @param withInsertTime
     * @param mapper
     *
     * @author shaohui
     * @date 2017年2月19日上午11:55:07
     * @since JDK 1.8
     */
    public <T> void insert(T object, boolean withInsertTime, DPObjectMapper<T> mapper) {
        if (object == null) {
            throw new NullPointerException("object");
        }
        if (mapper == null) {
            throw new NullPointerException("mapper");
        }
        insert(mapper.mapper(object), withInsertTime);
    }
    
    /**
     * 
     * 插入新的文档
     * 
     * @param object
     * @param mapper
     *
     * @author shaohui
     * @date 2017年2月19日上午11:57:33
     * @since JDK 1.8
     */
    public <T> void insert(T object, DPObjectMapper<T> mapper) {
        insert(object, true, mapper);
    }
    
    /**
     * 
     * 插入实体类
     * 
     * @param obj
     *
     * @author shaohui
     * @date 2016年7月3日下午5:31:20
     * @since JDK 1.8
     */
    public <T> void insertObject(T obj) {
        insertObject(obj, true);
    }
    
    /**
     * 
     * 插入实体类
     * 
     * @param obj
     * @param withInsertingTime
     *
     * @author shaohui
     * @date 2016年7月3日下午5:53:12
     * @since JDK 1.8
     */
    public <T> void insertObject(T obj, boolean withInsertingTime) {
        if (obj == null) {
            throw new NullPointerException("obj");
        }
        Class<? extends Object> clazz = obj.getClass();
        String tableName = clazz.getAnnotation(Table.class) == null ? null : clazz.getAnnotation(Table.class).name();
        if (StringUtils.isBlank(tableName)) {
            tableName = clazz.getSimpleName();
        }
        if (!tableName.equals(collection.getNamespace())) {
            throw new DPException("待插入实体类与文档不匹配");
        }
        Field[] fields = clazz.getDeclaredFields();
        if (fields.length == 0) {
            throw new IllegalArgumentException("class has no fields");
        }
        DPDocument insertingDocument = new DPDocument();
        for (Field field : fields) {
            Column column = field.getAnnotation(Column.class);
            String columnName = column == null ? null : column.name();
            if (StringUtils.isBlank(columnName)) {
                columnName = field.getName();
            }
            try {
                insertingDocument.append(columnName, field.get(obj));
            } catch (IllegalArgumentException | IllegalAccessException e) {
                throw new DPException("获取字段值失败", e);
            }
        }
        insert(insertingDocument, withInsertingTime);
    }
    
    /**
     * 
     * 批量插入实体类
     * 
     * @param objs
     *
     * @author shaohui
     * @throws IllegalAccessException 
     * @throws IllegalArgumentException 
     * @date 2016年7月3日下午6:12:18
     * @since JDK 1.8
     */
    public <T> void insertObjects(List<T> objs) throws IllegalArgumentException, IllegalAccessException {
        insertObjects(objs, true);
    }
    
    /**
     * 
     * 批量插入实体类
     * 
     * @param objs
     * @param withInsertingTime
     *
     * @author shaohui
     * @throws IllegalAccessException 
     * @throws IllegalArgumentException 
     * @date 2016年7月3日下午6:07:56
     * @since JDK 1.8
     */
    public <T> void insertObjects(List<T> objs, boolean withInsertingTime) throws IllegalArgumentException, IllegalAccessException {
        if (CollectionUtils.isEmpty(objs)) {
            throw new IllegalArgumentException("empty objs");
        }
        T firstItem = objs.get(0);
        if (firstItem == null) {
            throw new IllegalArgumentException("null item is not allowed");
        }
        Class<? extends Object> clazz = firstItem.getClass();
        String tableName = clazz.getAnnotation(Table.class) == null ? null : clazz.getAnnotation(Table.class).name();
        if (StringUtils.isBlank(tableName)) {
            tableName = clazz.getSimpleName();
        }
        if (!tableName.equals(collection.getNamespace())) {
            throw new IllegalArgumentException("待插入实体类与文档不匹配");
        }
        Field[] fields = clazz.getDeclaredFields();
        if (fields.length == 0) {
            throw new IllegalArgumentException("class to be inserted has no fields");
        }
        List<DPDocument> insertingDocuments = new ArrayList<>();
        int insertingSize = objs.size();
        for (int i = 0; i < insertingSize; i++) {
            insertingDocuments.add(new DPDocument());
        }
        for (Field field : fields) {
            if(!field.isAnnotationPresent(Column.class)){
                continue;
            }
            Column column = field.getAnnotation(Column.class);
            String columnName = column.name();
            if (StringUtils.isBlank(columnName)) {
                columnName = field.getName();
            }
            for (int i = 0; i < insertingSize; i++) {
                DPDocument insertingDocument = insertingDocuments.get(i);
                insertingDocument.append(columnName, field.get(objs.get(i)));
            }
        }
        insert(insertingDocuments, withInsertingTime);
    }
    
    /**
     * 
     * 返回原始的被包装的 {@link MongoCollection}<br>
     * 使用mongodb原始类来进行相关mongo操作，不推荐这么做，因为会导致后续的版本升级带来的重构难度<br>
     * 当使用该包装类不易实现目标时可考虑该方法，因为mongodb驱动的升级导致该方法随时有可能被移除
     *
     * @return
     * 
     * @author shaohui
     * @date 2017年1月24日 下午2:41:46
     */
    @Deprecated
    public MongoCollection<Document> toCollection() {
        return collection;
    }
    
    /**
     * 
     * 更新（最多单个记录）
     *
     * @param criteria
     * @param newObject
     * 
     * @author shaohui
     * @date 2016年4月14日 下午7:47:02
     */
    public DPWriteResult update(DPDocument criteria, DPDocument newObject) {
        return update(criteria, newObject, false, false, true);
    }
    
    /**
     * 
     * 更新
     *
     * @param criteria 查询条件
     * @param newObject 更新文档
     * @param upsert 不存在时是否插入
     * @param multiple 是否更新多个
     * 
     * @author shaohui
     * @date 2016年4月14日 下午7:39:11
     */
    public DPWriteResult update(DPDocument criteria, DPDocument newObject, boolean upsert, boolean multiple) {
        return update(criteria, newObject, upsert, multiple, true);
    }
    
    /**
     * 
     * 更新
     *
     * @param criteria 查询条件，不能为空
     * @param newDocument
     * @param upsert
     * @param multiple
     * @param withUpdateTime 是否更新（新增）更新时间字段
     * 
     * @author shaohui
     * @date 2016年4月14日 下午7:59:24
     */
    public DPWriteResult update(DPDocument criteria, DPDocument newDocument, boolean upsert, boolean multiple, boolean withUpdateTime) {
        if (criteria == null) {
            throw new NullPointerException("criteria");
        }
        if (newDocument == null) {
            throw new NullPointerException("newDocument");
        }
        if (withUpdateTime) { 
            if (newDocument.containsKey("$set")) {
                Object obj = newDocument.get("$set");
                if (obj != null || obj instanceof DPDocument) {
                    ((DPDocument) obj).append(DPSystemFields.UPDATE_TIME, new Date());
                }
            } else {
                newDocument.append(DPSystemFields.UPDATE_TIME, new Date());
            }
        }
        UpdateOptions updateOptions = new UpdateOptions();
        updateOptions.upsert(upsert);
        UpdateResult updateResult = null;
        if (multiple) {
            updateResult = collection.updateMany(criteria.toDocument(), newDocument.toDocument(), updateOptions);
        } else {
            updateResult = collection.updateOne(criteria.toDocument(), newDocument.toDocument(), updateOptions);
        }
        DPWriteResult writeResult = new DPWriteResult();
        writeResult.setMatchedCount(updateResult.getMatchedCount());
        writeResult.setEffectedCount(updateResult.getModifiedCount());
        writeResult.setWriteType(updateResult.getUpsertedId() == null ? DPWriteResultType.UPDATE : DPWriteResultType.INSERT);
        return writeResult;
    }
    
    /**
     * 
     * 更新记录，当记录不存在时插入记录
     * 
     * @param criteria
     * @param newObject
     * @return
     *
     * @author shaohui
     * @date 2016年7月10日下午8:41:15
     * @since JDK 1.8
     */
    public DPWriteResult upsert(DPDocument criteria, DPDocument newObject) {
        return update(criteria, newObject, true, false, true);
    }
}
